Tool Loop Agent
Abstract base Agent implementation — the canonical v6 ToolLoopAgent. Extend it; do not instantiate it. A concrete agent subclasses this (e.g. class MyAgent(...) : ToolLoopAgent<C, O>(...)) so the agent's identity, tools, lifecycle overrides, and DI live in one named type — never a bare ToolLoopAgent(...) held as a field.
Runs the model, executes tool calls, accumulates the message list, checks stopWhen after each step, surfaces lifecycle events. Manages the loop end-to-end (invariant I-7) — no manual loop in user code.
Per the v6 source comment in packages/ai/src/agent/tool-loop-agent.ts, the loop continues until:
A finish reason other than
tool-callsis returned, ORA tool that is invoked does not have an execute function, OR
A tool call needs approval, OR
A stop condition is met (default
stepCountIs(20)).
Approval flow (v6 RPC semantics)
When a tool's needsApproval(input, context) returns true:
Emit StreamEvent.ToolApprovalRequest in the stream.
Append ContentPart.ToolApprovalRequest to the assistant message.
End the loop — return control to the host with
GenerateResult.pendingApprovalspopulated.Host calls generate again with
messages = result.messages + toolApprovalResponseMessage(...).Loop resumes; approved tools execute; denied tools are skipped and the denial reason flows back to the model as a tool result.
Generation isn't kept "in flight" while the user decides — host can serialize, persist, transport, then resume.
Constructors
Properties
Port-level engine state machine (isStreaming, currentToolCalls, pendingApprovals, totalSteps, etc.). Distinct from the app-level host-specific orchestration state which is a host-specific reactive lifecycle surface layered on top. Renamed from state so subclasses can declare their own state member without member-hiding clashes.
Self-healing callback fired when a tool call's arguments fail to decode. Return a corrected call to retry, or null to surface StreamEvent.ToolError. See ToolCallRepairFunction.
Secret for HMAC-signing tool-approval requests (upstream v6.0.202 experimental_toolApprovalSecret). When set, every approval request the loop issues carries a signature over (approvalId, toolCallId, toolName, input), and a replayed approval is re-validated FAIL-CLOSED before the tool executes: missing/invalid signature throws AgentError.InvalidToolApprovalSignature, the input is re-validated against the tool's schema, and a tool that no longer requires approval is denied rather than run — so a client cannot forge, re-target, or input-swap an approval. Experimental upstream (can break in patches).
v6 CallSettings.frequencyPenalty agent-default.
Stable identifier for the agent, useful for telemetry, log routing, and the Agent.version / id / tools accessors v6 exposes (per historical parity gap #33). Implementations default to "agent"; override in the constructor or via class member to set a more specific tag ("chat-agent", "lineup-subagent", etc.).
Max tool calls executed concurrently within one step. Default unbounded — all of a step's tool calls run at once (latency = slowest tool, not the sum). Results are still applied to the message log in deterministic call order.
v6 CallSettings.presencePenalty agent-default.
Wire-level response constraint for providers that support it.
Telemetry for this agent's invocations (upstream v7 telemetry). Telemetry integrations registered globally via registerTelemetry observe every generate/stream call automatically; this setting adds call metadata (TelemetrySettings.functionId) or per-agent TelemetrySettings.integrations — which, when non-empty, REPLACE the global registrations for this agent's calls (upstream per-call semantics). Telemetry observes — an integration throw never alters loop behavior.
The tool surface this agent carries. Exposed at the interface so consumers can inspect / dispatch without casting to ToolLoopAgent.
Functions
Drive the engine. Each action either advances engineState or aborts the in-flight stream. Idempotent — calling ToolLoopAgentAction.Cancel when nothing is streaming is a no-op.
Streaming generation. Cold flow — starts when collected.